/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "MyTest"

#include <unistd.h>
#include <thread>

#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>

#include <gui/BufferQueue.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
#include <ui/FenceTime.h>
#include <ui/GraphicBuffer.h>

using namespace android;

// Macros for including the GLConsumer name in log messages
#define LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
#define LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
//#define LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
#define LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
#define LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)

class FakeCameraConsumer : public ConsumerBase {
public:
    //FakeCameraConsumer(const sp<IGraphicBufferConsumer>& consumer, bool userFenceSync, bool controlledByApp = false);
    FakeCameraConsumer(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);

    status_t setDefaultBufferSize(uint32_t width, uint32_t height);

//protected:

    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_SW_READ_RARELY | GraphicBuffer::USAGE_SW_WRITE_RARELY;

    // abandonLocked overrides the ConsumerBase method to clear
    // mCurrentTextureImage in addition to the ConsumerBase behavior.
    virtual void abandonLocked();

    // dumpLocked overrides the ConsumerBase method to dump FakeCameraConsumer-
    // specific info in addition to the ConsumerBase behavior.
    virtual void dumpLocked(String8& result, const char* prefix) const;

    // acquireBufferLocked overrides the ConsumerBase method to update the
    // mEglSlots array in addition to the ConsumerBase behavior.
    virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
            uint64_t maxFrameNumber = 0) override;

    // releaseBufferLocked overrides the ConsumerBase method to update the
    // mEglSlots array in addition to the ConsumerBase.
    virtual status_t releaseBufferLocked(int slot,
            const sp<GraphicBuffer> graphicBuffer,
            EGLDisplay display, EGLSyncKHR eglFence) override;

    status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber = 0);
    status_t releaseBuffer(int slot);

    status_t getGraphicBuffer(int slot, sp<GraphicBuffer> &graphicBuffer);

private:
   // freeBufferLocked frees up the given buffer slot. If the slot has been
    // initialized this will release the reference to the GraphicBuffer in that
    // slot and destroy the EGLImage in that slot.  Otherwise it has no effect.
    //
    // This method must be called with mMutex locked.
    virtual void freeBufferLocked(int slotIndex);

    uint32_t mDefaultWidth, mDefaultHeight;    
};

FakeCameraConsumer::FakeCameraConsumer(const sp<IGraphicBufferConsumer>& bq, bool isControlledByApp) :
    ConsumerBase(bq, isControlledByApp),
    mDefaultWidth(1),
    mDefaultHeight(1)
{
    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}

status_t FakeCameraConsumer::setDefaultBufferSize(uint32_t w, uint32_t h)
{
    Mutex::Autolock lock(mMutex);
    if (mAbandoned) {
        LOGE("setDefaultBufferSize: FakeCameraConsumer is abandoned!");
        return NO_INIT;
    }
    mDefaultWidth = w;
    mDefaultHeight = h;
    return mConsumer->setDefaultBufferSize(w, h);
}

void FakeCameraConsumer::freeBufferLocked(int slotIndex) {
    LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
    ConsumerBase::freeBufferLocked(slotIndex);
}

void FakeCameraConsumer::abandonLocked() {
    LOGV("abandonLocked");
    ConsumerBase::abandonLocked();
}

void FakeCameraConsumer::dumpLocked(String8& result, const char* prefix) const
{
    ConsumerBase::dumpLocked(result, prefix);
}

status_t FakeCameraConsumer::acquireBufferLocked(BufferItem *item,
        nsecs_t presentWhen, uint64_t maxFrameNumber) {
    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen,
            maxFrameNumber);
    if (err != NO_ERROR) {
        return err;
    }

    // If item->mGraphicBuffer is not null, this buffer has not been acquired
    // before, so any prior EglImage created is using a stale buffer. This
    // replaces any old EglImage with a new one (using the new buffer).
    if (item->mGraphicBuffer != nullptr) {
        int slot = item->mSlot;
        //mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
    }

    return NO_ERROR;
}

status_t FakeCameraConsumer::releaseBufferLocked(int buf,
        sp<GraphicBuffer> graphicBuffer,
        EGLDisplay display, EGLSyncKHR eglFence) {
    // release the buffer if it hasn't already been discarded by the
    // BufferQueue. This can happen, for example, when the producer of this
    // buffer has reallocated the original buffer slot after this buffer
    // was acquired.
    status_t err = ConsumerBase::releaseBufferLocked(
            buf, graphicBuffer, display, eglFence);
    //mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
    return err;
}

status_t FakeCameraConsumer::acquireBuffer(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber)
{
    Mutex::Autolock lock(mMutex);

    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen,
            maxFrameNumber);
    if (err != NO_ERROR) {
        return err;
    }

    // If item->mGraphicBuffer is not null, this buffer has not been acquired
    // before, so any prior EglImage created is using a stale buffer. This
    // replaces any old EglImage with a new one (using the new buffer).
    if (item->mGraphicBuffer != nullptr) {
        int slot = item->mSlot;
        //mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
    }

    return NO_ERROR;
}

status_t FakeCameraConsumer::releaseBuffer(int slot)
{
    Mutex::Autolock lock(mMutex);

    status_t err = ConsumerBase::releaseBufferLocked(
            slot, mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);

    return err;
}

status_t FakeCameraConsumer::getGraphicBuffer(int slot, sp<GraphicBuffer> &graphicBuffer)
{
    graphicBuffer = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}

//////////////////////////////////////////////////////////


#if 1

sp<FakeCameraConsumer> fakeCameraConsumer;

static void consumer_process_thread()
{
    while (1)
    {
        BufferItem item;
        status_t status = fakeCameraConsumer->acquireBuffer(&item, static_cast<nsecs_t>(0), 0);
        if (status == NO_ERROR)
        {
            sp<GraphicBuffer> graphicBuffer;// = fakeCameraConsumer->mSlots[item.mSlot].mGraphicBuffer;
            fakeCameraConsumer->getGraphicBuffer(item.mSlot, graphicBuffer);
            
            uint32_t* dataOut;
            graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&dataOut));

            //error: item.mFrameNumber is uint64_t type
            //printf("slot=%d, frameNum=%d, the data out is %d, dataOut=0x%x\n", item.mSlot, item.mFrameNumber, *dataOut, dataOut);

            printf("slot=%d, frameNum=%llu, the data out is %d, dataOut=0x%x\n", item.mSlot, item.mFrameNumber, *dataOut, dataOut);
            //printf("slot=%d, frameNum=%d, the data out is %d, dataOut=0x%x\n", item.mSlot, (int)item.mFrameNumber, *dataOut, dataOut);

            graphicBuffer->unlock();

            fakeCameraConsumer->releaseBuffer(item.mSlot);
        }

        usleep(1000000);
    }
}

int main(int argc, char** argv)
{
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);

    fakeCameraConsumer = new FakeCameraConsumer(consumer, false);

    sp<IServiceManager> serviceManager = defaultServiceManager();
    serviceManager->addService(String16("MyProducer"), IInterface::asBinder(producer));
    serviceManager->addService(String16("MyConsumer"), IInterface::asBinder(consumer));

    ProcessState::self()->startThreadPool();

    std::thread consumer_th(consumer_process_thread);

    IPCThreadState::self()->joinThreadPool();

    return 0;
}

#else

struct DummyConsumer : public BnConsumerListener {
    void onDisconnect() override
    {
        printf("producer is disconnected.\n");
    }

    void onFrameAvailable(const BufferItem& item) override 
    {
        printf("onFrameAvailable: framenumber=%llu.\n", item.mFrameNumber);
    }

    void onBuffersReleased() override 
    {

    }

    void onSidebandStreamChanged() override 
    {

    }

};

// void consumer_process_thread(void *arg)
// {
//     sp<IGraphicBufferConsumer> consumer = ;
// }

sp<IGraphicBufferConsumer> consumer;

void consumer_process_thread()
{
    /****************** Consumer ***********************/
    while (1)
    {
        //printf("#####################\n");

        BufferItem item;
        status_t status = consumer->acquireBuffer(&item, static_cast<nsecs_t>(0));
        if (status == NO_ERROR)
        {
            if (item.mGraphicBuffer != NULL)
            {
                //printf("##################1##\n");
                //if (item.mGraphicBuffer == NULL)
                //    printf("##################1#mGraphicBuffer=NULL#\n");
                //printf("##################1#item=0x%x#\n",item);
                //printf("##################1#item.mGraphicBuffer=0x%x#\n", reinterpret_cast<uint32_t *>(item.mGraphicBuffer));
                //printf("##################1##\n");
                uint32_t* dataOut;
                item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
                                reinterpret_cast<void**>(&dataOut));
                printf("the data out is %d\n", *dataOut);
                item.mGraphicBuffer->unlock();
                //printf("###############2######\n");                   
            }
            else
            {
                printf("item.mAcquireCalled=%d, slot=%d, item.mFrameNumber=%d\n", item.mAcquireCalled, item.mSlot, item.mFrameNumber);
            }

            printf("server get slot=%d, item.mFrameNumber=%d\n", item.mSlot, item.mFrameNumber);
        }

        consumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY,
            EGL_NO_SYNC_KHR, Fence::NO_FENCE);


        usleep(1000000);
    }
}

int main(int argc, char** argv)
{
    sp<IGraphicBufferProducer> producer;
    //sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);

    consumer->consumerConnect(new DummyConsumer, false);

    sp<IServiceManager> serviceManager = defaultServiceManager();
    serviceManager->addService(String16("MyProducer"), IInterface::asBinder(producer));
    serviceManager->addService(String16("MyConsumer"), IInterface::asBinder(consumer));

    ProcessState::self()->startThreadPool();


    //std::thread consumer_th(consumer_process_thread, (void *)consumer);
    std::thread consumer_th(consumer_process_thread);

    IPCThreadState::self()->joinThreadPool();

    return 0;
}

#endif